Server Vs Workstation GC

6 min read
Senior6 min read
Rapid overview

Server Vs Workstation GC

TL;DR

Server GC and Workstation GC are the two .NET garbage collector flavors: Server GC runs one parallel GC thread per core with larger heap segments to maximize throughput on multi-core hosts (the ASP.NET Core default), while Workstation GC uses a single GC thread and smaller segments to keep pauses short for interactive desktop apps. Pick Server GC for APIs and services, but measure — tiny CPU-capped containers sometimes do better on Workstation GC.

How it works

AspectServer GCWorkstation GC
GoalThroughput for multi-core serversResponsiveness for desktop/interactive apps
Threads1 GC thread per logical core (+ background GC)1 GC thread total (+ background GC)
Segment sizesLarger heap segments → fewer GCs, longer pausesSmaller segments → more frequent, shorter pauses
CompactionParallel, wideSerial, narrower
Default in ASP.NET CoreUsually enabledNot typical
Best forWeb APIs, services, batch jobs, high QPSWPF/WinForms, tools, dev utilities
Console.WriteLine(System.Runtime.GCSettings.IsServerGC); // true = Server GC

Server GC

  • On startup, CLR creates one GC worker per core. During a blocking GC, all managed threads hit a safepoint; GC workers run in parallel to mark/compact.
  • Background (concurrent) Gen2 collections run alongside the app. Gen0/Gen1 are still short stop-the-world.
  • Bigger segments (ephemeral & Gen2) reduce GC frequency under high allocation rates (typical on APIs parsing JSON, serializing, buffering).
  • Pauses can be longer, but total % time in GC is usually lower, boosting throughput.

Workstation GC

  • Designed to feel snappy on a single user’s machine.
  • Smaller segments → more frequent, shorter GCs.
  • Background GC also exists, but there’s no parallel army of GC workers per core.

Latency & modes (apply to both flavors)

  • Background GC (Gen2) is on by default; it overlaps some GC work with your app.
  • Latency modes via GCSettings.LatencyMode:
  • Interactive (balance; default for workstation).
  • Batch (maximize throughput; longer pauses OK—common with server GC).
  • SustainedLowLatency (reduce Gen2 frequency; good around critical windows).
  • NoGCRegion (attempt zero collections during a critical section—must pre-reserve memory; avoid if you’ll hit LOH or exceed reservation).

Example:

using System.Runtime;
GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;

Container & hosting notes (very relevant today)

  • .NET is container-aware: CPU/memory limits influence GC heuristics.
  • Environment toggles:
  • DOTNET_GCServer=1 (or legacy COMPlus_GCServer=1) → Server GC on.
  • Heap caps: DOTNET_GCHeapHardLimit or % variant to avoid over-commit.
  • If you CPU-cap a container (e.g., --cpus=2), Server GC will create GC workers according to the limit, not host machine total (Core 3.0+).
  • Small microservices with very tight limits sometimes perform better with Workstation GC (fewer GC worker threads contending). Measure.

Choosing the right one (simple rules)

Use Server GC when:

  • ASP.NET Core APIs / gRPC / background services on multi-core hosts.
  • High allocation rates (serialization, buffers, streams).
  • You prioritize throughput and can tolerate slightly longer pauses.

Use Workstation GC when:

  • UI/desktop apps where interaction latency matters.
  • Tiny sandboxed processes with strict CPU quotas (1 vCPU) where Server GC parallelism provides little benefit.

Tuning checklist for a trading/back-office service

  1. Ensure Server GC in production:
  • Set DOTNET_GCServer=1 in env or project runtimeconfig.
  • Verify with GCSettings.IsServerGC.
  1. Watch LOH churn (≥ 85 KB allocations): pool large buffers (ArrayPool<T>), stream with System.IO.Pipelines, use Utf8JsonReader.
  2. Latency windows (market open/rollover):
  • Temporarily set SustainedLowLatency or carefully use NoGCRegion.
  1. Measure, don’t guess:
  • dotnet-counters monitor System.Runtime
  • Key signals: Gen2 GC Count (should be rare), % Time in GC (low single digits), Allocated Bytes/sec (track spikes).
  1. Avoid manual GC.Collect() in steady state—only for controlled maintenance (e.g., one-off LOH compaction during off-peak).

Pitfalls & gotchas (the “senior” bits)

  • Server GC isn’t always faster if your service is CPU-starved (1 core) or allocation-light. Validate with load tests.
  • Background Gen2 is not free: if promotion pressure is high (many objects reaching Gen2), you’ll still see noticeable pauses. Reduce promotions by:
  • Shortening object lifetimes (don’t cache what you don’t need).
  • Pooling and reusing (buffers, parsers).
  • Pinned memory (interop) prevents compaction; pin briefly or copy into a staging buffer to keep the SOH healthy.
  • LOH fragmentation grows pause times over days/weeks. If you must allocate ≥85 KB often, restructure to chunk (e.g., 64 KB + 32 KB) or reuse pooled slabs.

Answers you can give quickly

Q: * Why is Server GC better for ASP.NET services? A:* “It uses parallel GC workers per core and larger segments, so collections are less frequent and total GC time is lower. That boosts throughput under high allocation rates typical for APIs.”
Q: * When might Workstation GC be preferable on a server? A:* “Very small, CPU-constrained processes—e.g., 1 vCPU containers—where Server GC’s parallelism offers little benefit and shorter workstation pauses feel better. But I’d measure both.”
Q: * How do you confirm and monitor this in prod? A:* “GCSettings.IsServerGC at startup, and dotnet-counters/App Insights to watch Gen2 counts, % time in GC, and allocation rate. If Gen2 climbs or LOH grows, we pool buffers and reduce promotions.”

If you want, I can give you a 30-second script you can say verbatim when they ask “Server vs Workstation GC—when and why?”


Quick recall Q&A

Q: How do you switch between Server and Workstation GC?

Set DOTNET_GCServer=1 (or configure in runtimeconfig) for Server GC. Without it, Workstation GC is used by default for desktop apps. Always verify with GCSettings.IsServerGC.

Q: Why is Server GC ideal for web APIs?

It creates a GC worker per core and uses larger segments, reducing collection frequency and keeping throughput high under heavy allocation workloads common in APIs.

Q: When would Workstation GC outperform Server GC?

In small, CPU-limited containers or interactive desktop apps where shorter individual pauses matter more than raw throughput. Always measure both modes.

Q: Do latency modes differ between Server and Workstation GC?

Both support GCSettings.LatencyMode options (Interactive, Batch, SustainedLowLatency, NoGCRegion). Choose based on workload, not GC flavor.

Q: How does containerization affect GC choice?

.NET respects container CPU/memory limits when sizing GC segments and threads. If you limit CPUs, Server GC creates fewer worker threads accordingly.

Q: How do you monitor GC mode effectiveness?

Track % Time in GC, Gen2 counts, and LOH size via dotnet-counters or App Insights. Compare metrics when toggling between modes to justify the configuration.

Q: Can you mix modes within the same process?

No. GC mode is a process-wide setting configured at startup. You can’t run Server GC for some components and Workstation GC for others.

Q: How do pinned objects behave under each mode?

Pinning affects compaction regardless of GC mode. However, Server GC’s larger segments mean fragmentation can be more noticeable if you pin frequently.

Q: Does background GC behave differently between modes?

Server GC runs background Gen2 collections in parallel. Workstation GC also supports background GC but with fewer worker threads, so concurrency benefits are smaller.

Q: What’s your quick pitch comparing the two?

“Server GC maximizes throughput on multi-core servers via parallel collections; Workstation GC prioritizes responsiveness with shorter pauses. Choose based on workload and validate with GC metrics.”

See also